本系列文章已重新編修,並在加入部分 ES6 新篇章後集結成書,有興趣的朋友可至天瓏書局選購,感謝大家支持。
購書連結 https://www.tenlong.com.tw/products/9789864344130
讓我們再次重新認識 JavaScript!
在上一篇介紹過 JavaScript 的 true
與 false
奧妙之處 (?) 後,今天就來繼續談談流程控制的部分。
在開始今天的內容之前,我們先來看個在網路上流傳多年的老笑話:
工程師某日在下班前接到老婆的電話:
老婆說:「下班買十顆包子回家,如果看到賣西瓜的,就買一個。」
晚上工程師回到家,老婆看了就問:「怎麼只買了一個包子?」
工程師:「因為看到賣西瓜的。」
如果你不懂工程師笑點的話,那麼恭喜你有個正常人的腦袋。 XD
JavaScript 用來控制流程的「條件語法」指的是,當指定的條件為 true
時,就會執行後續所指定的指令。 而 JavaScript 的條件語法有兩種︰ if...else
和 switch
。
if...else
就如同字面上一樣,「如果」怎樣怎樣,就做某件事,「否則」做另一件事,語法像這樣︰
if ( 條件式 ){
// 某條件成立會執行這個 { } 區塊中的內容
// ...
}
else{
// 否則執行 else 這個 { } 區塊中的內容
// else 可以有也可以沒有
}
上面的「條件式」可以是 true
或 false
的表達式。 還記得前一篇說的嗎?
「JavaScript 所有資料都可以透過 ToBoolean
轉換成 true
或 false
」
轉換的規則請看前一篇 Day 08 運算式與運算子 [3]: Boolean 的真假判斷。
除了 if...else
之外,你也可以使用 else if
來新增條件:
if ( 條件式 ){
// 某條件成立會執行這個 { } 區塊中的內容
// ...
}
else if( 條件式2 ){
// ...
}
else if( 條件式3 ){
// ...
}
else if( 條件式4 ){
// ...
}
else{
// 若上面所有條件判斷都是 false ,才會執行 else 這個 { } 區塊中的內容
}
else if
基本上沒有數量的限制,但為了程式的可讀性與維護性,還是盡量減少 if...else
的條件數量喔。
所以回到前面笑話,寫成程式就會寫成這樣:
// 下班買十顆包子回家,如果看到賣西瓜的,就買一個。
var buy = "十顆包子";
if( 看到賣西瓜的 ) {
buy = "一個包子";
}
然後回家你就準備跪算盤了。
switch
這裏要講的 switch
不是任天堂那個 switch,而是流程判斷的另一個語法。
前面我們有提到,當你要做的條件判斷很多的時候,可能會寫成這樣:
if ( 條件式 ){
// 某條件成立會執行這個 { } 區塊中的內容
// ...
}
else if( 條件式2 ){
// ...
}
else if( 條件式3 ){
// ...
}
else if( 條件式4 ){
// ...
}
else{
// 若上面所有條件判斷都是 false ,才會執行 else 這個 { } 區塊中的內容
// ...
}
像這樣,假設 1~3 月春天,4~6 月夏天,7-9 月秋天,10-12 月冬天,
而我們要從月份來判斷季節,用 if...else
可以這樣寫:
var month = 12;
// 註:Math.ceil(n) 代表將 n 數值無條件進位
if( Math.ceil( month/3 ) === 1 ){
console.log('春天');
}
else if( Math.ceil( month/3 ) === 2 ){
console.log('夏天');
}
else if( Math.ceil( month/3 ) === 3 ){
console.log('秋天');
}
else if( Math.ceil( month/3 ) === 4 ){
console.log('冬天');
}
else{
console.log('月份錯誤');
}
像這樣,如果不幸是冬天的情況下,你的 Math.ceil( month/3 )
至少要做四次,且程式碼太長也不好維護。
如果改用 switch
的語法,可以這樣寫:
var month = 12;
switch ( Math.ceil(month/3) ){
case 1:
console.log('春天');
break;
case 2:
console.log('夏天');
break;
case 3:
console.log('秋天');
break;
case 4:
console.log('冬天');
break;
default:
console.log('月份錯誤');
break;
}
程式碼看起來是不是簡潔很多呢?
以上面的範例來說, switch
括號內的語法可能是運算式或是某個變數、值,像上面就是 Math.ceil(month/3)
。
接著會進入 case
來判斷,若switch
括號內的結果剛好是 case
後面的「值」,則會執行 case
區塊內的指令。
而 default
的區塊就是當上面所有 case
都不成立的時候會執行。
需要注意的是,case
區塊並不像 if...else
用大括號 { }
來切分區塊的,而是用 break
指令。
如果沒有加上 break
且 month = 1
的情況下會像這樣:
var month = 1;
switch ( Math.ceil(month/3) ){
case 1:
console.log('春天');
case 2:
console.log('夏天');
case 3:
console.log('秋天');
case 4:
console.log('冬天');
default:
console.log('月份錯誤');
}
像這樣,程式就會一路綠燈開到底了,除非你中間插入 break
語法。
但「適當地」利用 break
語法的話,你也可以這樣做:
var month = 1;
switch ( month ){
case 1:
case 2:
case 3:
console.log('春天');
break;
case 4:
case 5:
case 6:
console.log('夏天');
break;
case 7:
case 8:
case 9:
console.log('秋天');
break;
case 10:
case 11:
case 12:
console.log('冬天');
break;
default:
console.log('月份錯誤');
}
所以除非你很清楚你要的結果,否則強烈建議 case
區塊結束前一定要加入 break
來終止。
(條件式) ? A : B
講完 if...else
和 switch
之後,我才想起前面的「運算子」系列我遺漏了一個很有趣的東西:「三元運算子」。
三元運算子,又稱「條件運算子」,分別以 「問號」 ?
以及「冒號」:
所組合而成:
(條件) ? [數值1] : [數值2];
如果 「條件」 為 true
,此時回傳 數值1, 否則回傳 數值2。
實際使用的方式:
var status = (age >= 18) ? '成人' : '小孩';
像這樣會先判斷變數 age
有沒有大於或等於 18
。
如果有的話, status
的值會是 '成人'
,否則就是 '小孩'
。
接下來看到迴圈的部分。
所謂「迴圈」指的是,想要重複做某件事,而數值會依次數有「遞增」或「遞減」的變化來完成退出的條件。
什麼意思? 舉個例子。
假設我們要用 console.log()
從 1
印到 10
,在沒有使用迴圈的情況下:
console.log(1);
console.log(2);
console.log(3);
console.log(4);
console.log(5);
console.log(6);
console.log(7);
console.log(8);
console.log(9);
console.log(10);
同樣的東西你得寫十次,看起來很蠢對吧?
在 JavaScript 裡面,迴圈的常見語法有 for
以及 while
兩種。
那麼我們就來看看改用「迴圈」之後的寫法可以多簡便。
for
迴圈一個簡單的 for
迴圈語法會是這樣:
var i;
for (i = 0; i < 10; i++) {
// 做某件事
}
小括號 ( )
的內容可以分成三個部分:
畫紅線的是「初始值」,用來初始化 for
迴圈中的計數器。
你可能會看過有人這樣寫:
for (var i = 0; i < 10; i++) {
// 做某件事
}
雖然在這裡可以用 var
來宣告變數,但要小心,這裡的變數並不是專屬 for
迴圈內的變數,變數 i
的有效範圍其實跟 for
迴圈是相同的。
綠線的部分是「執行迴圈的條件」,指的是當滿足這個條件 (結果為 true
) 的時候,就會進入大括號 { }
的區塊,然後執行內部程式。
藍線的部分是,在每一次執行完大括號 { }
區塊的程式碼之後,會執行這段程式碼。
換言之,假設我們要用 console.log()
從 1
印到 10
,用 for
的寫法就是:
for (var i = 1; i <= 10; i++) {
console.log(i);
}
while
迴圈然後我們看看 while
迴圈。
把剛剛從 1
印到 10
,改用 while
的寫法就是:
var i = 1;
while ( i <= 10 ){
console.log( i );
i++;
}
可以看到, while
迴圈的語法就顯得單純一些。
括號 ()
內代表的是「執行迴圈的條件」,指的是當滿足這個條件 (結果為 true
) 的時候,就會進入大括號 { }
的區塊,然後執行內部程式。
與 for
相比,兩者在執行時的原理其實大同小異。
然而需要注意的是,不管 for
或 while
迴圈,「結束的條件」是很重要的。
在執行迴圈的時候,若是「執行條件」沒有設定好,很容易變成「無窮迴圈」在裡面無限空轉,像:
// i 不管怎麼樣都會大於等於 0
for (var i = 0; i >= 0; i++) {
console.log(i);
}
或是
var i = 1;
// 結束迴圈時, i 的值仍然是 1
while ( i <= 10 ){
console.log( i );
}
因為條件不管怎麼樣都會成立,寫成這樣的話你就跳不出迴圈啦!
break
與 continue
剛才我們看過了 for
與 while
基本語法,
那麼假如我們想要跳過其中幾次,或是想要提早離開迴圈,有什麼方式呢?
這時候你會需要 break
與 continue
。
兩者的功能差別:
break
會直接跳離迴圈continue
會跳過一次,然後繼續下一次迴圈。所以說,假設我們想要印出 1 ~ 10 的所有數字,但跳過 3 的倍數 :
for (var i = 1; i <= 10; i++) {
// i 能被 3 整除表示 i 是 3 的倍數,遇到 continue 就會跳過這次
if( i % 3 === 0){
continue;
}
console.log(i);
}
有個陣列,假設裡面包含一堆 0
與若干其他數字,而我們不知道裡面內容。
我們想要找出不是「0」的「第一個數字」是哪一個:
// 假設陣列是這樣包含一堆 0 與若干其他數字
var arr = [0,0,0,0,7,0,9,0,4,8,0];
for(var i = 0; i < arr.length; i++ ) {
if( arr[i] !== 0 ){
// 找到那個不是 0 的數字,印出後退出迴圈
console.log( arr[i] );
break;
}
}
for
與 while
兩者的差異點在大多數情況之下,for
與 while
迴圈兩者能做的事情是一樣的。
那麼有沒有什麼事情是非 for
不可或非 while
不可呢?
在今天文章的最後,我簡單做個區別:
for
迴圈的使用情境,大多是用在迴圈執行次數「明確」的狀態while
迴圈則剛好相反,當迴圈執行次數「不確定」的時候更適合。什麼意思? 迴圈的特性都是,「只要指定條件是 true
」就可以進入迴圈區塊對吧?
因為 for
迴圈同時包含了「初始值」、「條件」以及「結束迴圈的更新」三個部分,使得它的執行次數你可以一眼就看出來; 而 while
迴圈只包含「條件」的部分。
用一個我很喜歡的例子來說,假設我們要把「大樂透」的電腦選號規則程式化:「從 1 ~ 49 中選 6 個不重複號碼」
用 while
迴圈的話,你可以這樣寫:
var lottery = [];
var n;
// 直到陣列 lottery 選滿 6 球
while(lottery.length < 6) {
// 取一隨機 1 ~ 49 數字
n = Math.floor( Math.random() * 49 ) + 1;
// 如果選出來的 n 不存在,就放入陣列
if( lottery.indexOf( n ) === -1 ){
lottery.push( n );
}
}
思考一下,在這樣的規則下,要用 for
迴圈來寫,可以怎麼寫?
就留待各位自行想像囉 XD
那麼以上就是今天分享的內容,謝謝收看。
if( lottery.indexOf( n ) !== -1 ){
lottery.push( n );
}
應該是 lottery.indexOf( n ) === -1 才對
不然一直無窮迴圈下去了。
哈哈哈,謝謝提醒,註解打不存在就下意識寫 !==
了,已修正
不客氣,也感謝你的文章讓我可以學習並複習。
範例中好像有錯字
var buy = "十顆包子";
if( 看到賣西瓜的 ) {
buy = "一個包子"; <= 一個西瓜才對
}
本題工程師解析老婆的話的意思為:
1.買十個包子
2.如果「看到賣西瓜的」,改買一個包子即可。
「看到賣西瓜的」為條件,並非執行買一個西瓜唷~。
你是正常人的腦袋XD
您好:
請問
for (var i = 1; i <= 10; i++) {
與
for ( i = 1; i <= 10; i++) {
最後的 差異是?
謝謝!
你好,要看你的 for
迴圈放在哪裡。
如果是在全域範圍下,那有沒有 var
其實沒差,因為最後 i
都會變成全域變數。 如果 for 迴圈在 function scoped 內,加了 var
就不會變成全域變數。
但以現在多數瀏覽器都支援的情況來說,建議都用 let
來作替代。
謝謝!
您好:不好意思
第三章,有一句話
所有沒有透過 var 宣告的變數都會自動變成全域變數。
各樣感覺 有點衝突:在function(){ 下:
var x=1 ; //非全域
y=2; //全域
}
?謝謝!
你好,你可以試著執行這段程式
function func(){
var x = 1;
y = 2;
}
func();
然後再分別 console
出 x
與 y
的內容就知道什麼意思囉。